using System;
using System.IO;
using System.Collections.Generic;
using System.Text;

using Weazel.Microblaze.Simulator.Engine;
using Weazel.Microblaze.Simulator.Components.Opb;

namespace Weazel.Microblaze.Simulator.Components.GenericRam
{
    public class GenericRam
      : OpbSlaveBase
    {
        private bool isReadOnly = false;

        private bool dump = true;
        private Stream stream;
        private StreamWriter writer;

        private const uint BYTE_MASK = 0xff;

        private byte[] content;

        public GenericRam(Simulator.Engine.SimulationContext simulator, uint low, uint high, bool isReadOnly)
            : base(simulator)
        {
            this.LowAddress = low;
            this.HighAddress = high;
            this.isReadOnly = isReadOnly;

            content = new byte[HighAddress - LowAddress];
        }

        public override string GetStatusString()
        {
            return "n/a";
        }

        public override void Run()
        {
            while(true)
                WaitClock();
        }

        public override void Load(byte[] bytes, uint address)
        {
            if (address + bytes.Length >= HighAddress)
                throw new MemoryException(Context, MemoryException.ErrorType.LoadError);

            for (int i = 0; i < bytes.Length; i++)
            {                
                uint localAddress = (uint) (address + i) - LowAddress;
                content[localAddress] = bytes[i];
            }
        }

        

        public override void CleanUp()
        {
            if (dump)
            {
                if(writer != null)
                    writer.Close();

                if(writer != null)
                    stream.Close();
            }

        }

        private void DumpEntry(bool isStore, int bytes, uint address, uint value)
        {
            /*
            if (isStore)
                writer.WriteLine("{0,8:X8} Store ({1}) : 0x{2,8:X8} at 0x{3,8:X8}", simulator.CyclesExecuted, bytes, value, address);
            else
                writer.WriteLine("{0,8:X8} Load  ({1}) : 0x{2,8:X8} at 0x{3,8:X8}", simulator.CyclesExecuted, bytes, value, address);
             * */
        }

        public override void BeginTransaction(Transaction transaction)
        {
            if(transaction.Type == Transaction.TransactionType.Write && isReadOnly)
                throw new MemoryException(Context, MemoryException.ErrorType.WriteErrorRom, transaction.Address);

            // decode address
            uint localAddress = transaction.Address - LowAddress;

            if (transaction.Type == Transaction.TransactionType.Read)
            {
                switch (transaction.Bytes)
                {
                    case 1: 
                        transaction.Value = content[localAddress];
                        break;
                    case 2:
                        transaction.Value = (ushort)
                            ((ushort)(content[localAddress + 2] << 8) |
                            (ushort)(content[localAddress + 3]));
                        break;
                    case 4:
                        transaction.Value =
                            (uint)(content[localAddress + 0] << 24) |
                            (uint)(content[localAddress + 1] << 16) |
                            (uint)(content[localAddress + 2] << 8) |
                            (uint)(content[localAddress + 3]);
                        break;

                    default:
                        throw new NotSupportedException();
                }
            }
            else if (transaction.Type == Transaction.TransactionType.Write)
            {
                switch (transaction.Bytes)
                {
                    case 1:
                        content[localAddress] = (byte) transaction.Value;
                        break;
                    case 2:
                        content[localAddress + 0] = (byte)((transaction.Value >> 8) & BYTE_MASK);
                        content[localAddress + 1] = (byte)(transaction.Value & BYTE_MASK);
                        break;
                    case 4:
                        content[localAddress + 0] = (byte)((transaction.Value >> 24) & BYTE_MASK);
                        content[localAddress + 1] = (byte)((transaction.Value >> 16) & BYTE_MASK);
                        content[localAddress + 2] = (byte)((transaction.Value >> 8) & BYTE_MASK);
                        content[localAddress + 3] = (byte)((transaction.Value & BYTE_MASK));
                        break;

                    default:
                        throw new NotSupportedException();
                        
                }
            }

            transaction.Status = Transaction.TransactionStatus.Complete;
        }

        /*
        public override MemoryAccessStatus WriteWord(uint address, uint value)
        {
            if (isReadOnly)
            {
                throw new MemoryException(Simulator, MemoryException.ErrorType.WriteErrorRom, address);
            }

            // decode address
            uint localAddress = address - LowAddress;

            // break up value and write ..
            content[localAddress + 0] = (byte)((value >> 24) & BYTE_MASK);
            content[localAddress + 1] = (byte)((value >> 16) & BYTE_MASK);
            content[localAddress + 2] = (byte)((value >> 8) & BYTE_MASK);
            content[localAddress + 3] = (byte)((value & BYTE_MASK));

            //     Console.WriteLine("Storing value 0x{0:X} at address 0x{1:X}", value, address);

            // DumpEntry(true, 4, address, value);
            return MemoryAccessStatus.Ok;
        }

        public override MemoryAccessStatus ReadWord(uint address, out uint value)
        {
            // decode address
            uint localAddress = address - LowAddress;

            value =
              (uint)(content[localAddress + 0] << 24) |
              (uint)(content[localAddress + 1] << 16) |
              (uint)(content[localAddress + 2] << 8) |
              (uint)(content[localAddress + 3]);

            DumpEntry(false, 4, address, value);

            return MemoryAccessStatus.Ok;
        }

        public override MemoryAccessStatus ReadHalfWord(uint address, out ushort value)
        {
            // decode address
            uint localAddress = address - LowAddress;

            value = (ushort)
              ((content[localAddress + 2] << 8) |
              (content[localAddress + 3]));

            return MemoryAccessStatus.Ok;
        }

        public override MemoryAccessStatus WriteHalfWord(uint address, ushort value)
        {
            if (isReadOnly)
            {
                throw new MemoryException(Simulator, MemoryException.ErrorType.WriteErrorRom, address);
            }

            // decode address
            uint localAddress = address - LowAddress;

            // break up value and write ..
            content[localAddress + 0] = (byte)((value >> 8) & BYTE_MASK);
            content[localAddress + 1] = (byte)(value & BYTE_MASK);

            return MemoryAccessStatus.Ok;
        }

        public override MemoryAccessStatus ReadByte(uint address, out byte value)
        {
            // decode address
            uint localAddress = address - LowAddress;

            value = content[localAddress];

            return MemoryAccessStatus.Ok;
        }

        public override MemoryAccessStatus WriteByte(uint address, byte value)
        {
            if (isReadOnly)
            {
                throw new MemoryException(Simulator, MemoryException.ErrorType.WriteErrorRom, address);
            }

            // decode address
            uint localAddress = address - LowAddress;
            content[localAddress] = value;

            return MemoryAccessStatus.Ok;
        }
         * */
    }
}
